#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include "util.hh"
#include "conf.hh"
#include "data.hh"
#include "client.hh"
#include "watch.hh"

namespace cloud
{

class detector
{
public:
    detector()
        : _back_dirs(config::instance().back_dirs())
        , _gap_time(config::instance().gap_time())
        , _data_mgr(data_manager::instance())
        , _http_cli(client::instance())
        , _watcher(watcher::instance())
    {
        for (auto& dir : _back_dirs)
            util::dir(dir).create();

        _watcher.regist(
            std::bind(&detector::handler_create, this, std::placeholders::_1),
            std::bind(&detector::handler_modify, this, std::placeholders::_1),
            std::bind(&detector::handler_delete, this, std::placeholders::_1)
        );

        easylog::info("detector model created");
    }

    void run()
    {
        std::vector<std::string> paths;
        for (auto& dir : _back_dirs)
        {
            const auto& ps = util::dir(dir).rscan();
            paths.insert(paths.end(), ps.begin(), ps.end());
        }

        // 程序启动时先将文件全部检查一遍，之后监测目录
        for (auto& bpath : paths)
        {
            if (util::path::isdir(bpath))
                continue;

            backup_info* info = _data_mgr.get_by_bpath(bpath);

            // 能获取到info说明是老文件，获取不到说明是程序退出时，新增的新文件
            if (info)
            {
                time_t lm_time = util::file(bpath).mtime();

                if (info->mtime == lm_time)
                    continue;
            }
            else
            {
                // info不存在就将其插入信息管理器
                info = _data_mgr.insert(backup_info(bpath));
                easylog::info("detect a new file so can't get its info");
            }

            if (!info)
            {
                easylog::error("insert new file into data mgr failed");
                continue;
            }

            // 或者存在但文件修改时间更新，在程序启动之前发生了修改，立即上传
            std::thread([&]()
            {
                _http_cli.upload_by_bpath(bpath);
            }).detach();

            info->mtime = util::file(bpath).mtime();
        }

        // 检查完毕，正式监控本地目录
        _watcher.watch();
    };


public:
    void handler_create(const std::string& bpath)
    {
        backup_info* info = _data_mgr.insert(bpath);

        if (!info)
        {
            easylog::error("create info failed {}", bpath);
            return;
        }

	// 创建文件时，文件大小一般都是零
        // if (info->fsize > 0)
        {
            if (!_http_cli.upload_by_bpath(bpath))
                easylog::warn("handle create event failed");
            else
				easylog::info("handle create event succ");
        }
    }

    void handler_modify(const std::string& bpath)
    {
        backup_info* info = _data_mgr.get_by_bpath(bpath);

        if (!info)
        {
            if ((info = _data_mgr.insert(backup_info(bpath))) == nullptr)
            {
				easylog::warn("get info failed {}", bpath);
                return;
            }
        }

        time_t mtime = util::file(bpath).mtime();

        // if (info->mtime + _gap_time < mtime)
        {
            if (!_data_mgr.update(bpath))
            {
                easylog::warn("update info failed {}", bpath);
                return;
            }

            info->mtime = mtime;

            if (!_http_cli.upload_by_bpath(bpath))
                easylog::warn("handle modify event failed");
            else
                easylog::info("handle modify event succ");
        }
        // else
        // {
        //     easylog::debug("upload file still gap");
        // }
    }

    void handler_delete(const std::string& bpath)
    {
        backup_info* info = _data_mgr.get_by_bpath(bpath);

        if (!info)
        {
            easylog::error("get info failed {}", bpath);
            return;
        }
        if (!_data_mgr.delete_by_bpath(bpath))
        {
            easylog::warn("delete info failed {}", bpath);
            return;
        }
        if (!_http_cli.delete_by_bpath(bpath))
        {
            easylog::warn("handle delete event failed {}", bpath);
            return;
        }

		easylog::info("handle delete event succ");
    }

private:
    std::vector<std::string> _back_dirs;
    size_t _gap_time;
    data_manager& _data_mgr;
    client& _http_cli;
    watcher& _watcher;
};

}
